/*
 * PacketFileMediaSink.cpp
 *
 *  Created on: 2016年10月10日
 *      Author: zhengboyuan
 */

#include "PacketFileMediaSink.h"
#include "TFileUtil.h"
#include "CLog.h"
#include "Path.h"
#include "TimeHelper.h"
#include "FfmpegUtil.h"
#include "PacketFile.h"
#include "TStringCast.h"
#include "MediaFormatJson.h"


namespace av
{


PacketFileMediaSink::PacketFileMediaSink():
	m_fmt(),
    m_fileName(),
    m_maxDuration(),
    m_changeFile(),
	m_file(),
	m_idxFile()
{

}

PacketFileMediaSink::~PacketFileMediaSink()
{
    close();
}

bool PacketFileMediaSink::open(const char* filename)
{
    if (isOpen())
    {
        close();
    }

    m_fileName = filename;
    m_idxName = m_fileName + ".idx";

    start();

    return true;
}

void PacketFileMediaSink::close()
{
    if (isRunning())
    {
        stop();
    }

    closeFile();
}

bool PacketFileMediaSink::isOpen()
{
    return isRunning();
}

bool PacketFileMediaSink::openFile(const char* filename, const MediaFormat& fmt)
{
    if (strlen(filename) <= 0)
    {
        return false;
    }

    if (!m_fmt.isValid())
    {
        return false;
    }

    if (!openFile())
    {
        return false;
    }

    return true;
}

const char* PacketFileMediaSink::getFile()
{
    return m_fileName.c_str();
}

int PacketFileMediaSink::run()
{
    while (!m_canExit)
    {
        AVPacketPtr pkt;
        m_pktQueue.pop(pkt, -1);
        if (pkt)
        {
            handlePacket(pkt);
        }
    }
    return 0;
}

void PacketFileMediaSink::doStop()
{
    m_pktQueue.cancelWait();
}


int PacketFileMediaSink::setFile(const char* filename)
{
    if (filename == NULL)
    {
        m_fileName.clear();
    }
    else
    {
        m_fileName = filename;
        m_idxName = m_fileName + ".idx";
    }

    {
        comn::AutoCritSec lock(m_cs);
        if (isFileOpen())
        {
            m_changeFile = true;
        }
    }

    m_pktQueue.cancelWait();

    return 0;
}

void PacketFileMediaSink::onMediaFormat(const MediaFormat& fmt)
{
    m_fmt = fmt;
}

void PacketFileMediaSink::onMediaPacket(AVPacketPtr& pkt)
{
    if (!pkt)
    {
        return;
    }

    m_pktQueue.push(pkt);
}

void PacketFileMediaSink::onMediaEvent(int event)
{
    // pass
}

bool PacketFileMediaSink::openFile()
{
	m_file = fopen(m_fileName.c_str(), "wb");
	if (!m_file)
	{
		return false;
	}

	m_idxFile = fopen(m_idxName.c_str(), "wb");
	if (!m_idxFile)
	{
		fclose(m_file);
		m_file = NULL;
		return false;
	}

	Json::Value value;
	MediaFormatJson::toJson(m_fmt, value);
	std::string text = comn::StringCast::toString(value);

	PacketHeader hdr;
	memset(&hdr, 0, sizeof(hdr));
	hdr.tag = kFormat;
	hdr.size = text.length();
	fwrite((char*)&hdr, 1, sizeof(hdr), m_file);

	fwrite(text.c_str(), 1, text.length(), m_file);

    return true;
}

void PacketFileMediaSink::closeFile()
{
	if (m_file)
	{
		fclose(m_file);
		m_file = NULL;
	}

	if (m_idxFile)
	{
		fclose(m_idxFile);
		m_idxFile = NULL;
	}

    m_changeFile = false;

    m_videoRectifier.reset();
    m_audioRectifier.reset();

}

bool PacketFileMediaSink::isFileOpen()
{
    return (m_file != NULL);
}


void PacketFileMediaSink::writeVideo(AVPacketPtr& inPkt)
{
	int64_t ts = m_videoRectifier.rectify(inPkt->pts);

	PacketIndex index;
	index.offset = ftell(m_file);
	index.type = inPkt->stream_index;
	index.timestamp = ts;
	index.keyframe = (inPkt->flags & AV_PKT_FLAG_KEY);
	//comn::FileUtil::write((char*)&index, sizeof(index), m_idxName.c_str(), true);
	fwrite((char*)&index, 1, sizeof(index), m_idxFile);
	fflush(m_idxFile);

	PacketHeader hdr;
	memset(&hdr, 0, sizeof(hdr));
	hdr.tag = kPacket;
	hdr.type = inPkt->stream_index;
	hdr.duration = inPkt->duration;
	hdr.flags = inPkt->flags;
	hdr.ts = ts;
	hdr.size = inPkt->size;

	fwrite((char*)&hdr, 1, sizeof(hdr), m_file);

	fwrite(inPkt->data, 1, inPkt->size, m_file);

	fflush(m_file);
}

void PacketFileMediaSink::writeAudio(AVPacketPtr& inPkt)
{
	int64_t ts = m_videoRectifier.rectify(inPkt->pts);

	PacketIndex index;
	index.offset = ftell(m_file);
	index.type = inPkt->stream_index;
	index.timestamp = ts;
	index.keyframe = (inPkt->flags & AV_PKT_FLAG_KEY);
	//comn::FileUtil::write((char*)&index, sizeof(index), m_idxName.c_str(), true);
	fwrite((char*)&index, 1, sizeof(index), m_idxFile);
	fflush(m_idxFile);

	PacketHeader hdr;
	memset(&hdr, 0, sizeof(hdr));
	hdr.tag = kPacket;
	hdr.type = inPkt->stream_index;
	hdr.duration = inPkt->duration;
	hdr.flags = inPkt->flags;
	hdr.ts = ts;
	hdr.size = inPkt->size;


	fwrite((char*)&hdr, 1, sizeof(hdr), m_file);
	fwrite(inPkt->data, 1, inPkt->size, m_file);
	fflush(m_file);
}

void PacketFileMediaSink::handlePacket(AVPacketPtr& pkt)
{
    if (m_changeFile)
    {
		closeFile();
    }

    if (!m_fileName.empty() && m_fmt.isValid())
    {
        if (!isFileOpen())
        {
			if (!openFile(m_fileName.c_str(), m_fmt))
			{
				return;
			}
        }
    }

    if (pkt->stream_index == 0)
    {
        writeVideo(pkt);
    }
    else
    {
        writeAudio(pkt);
    }
}



} /* namespace av */
